﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace TooManyNamespaces.Fluent.Utilities
{
    public static class Extensions
    {
        public static void Each<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
        {
            foreach (var item in source)
            {
                action.Invoke(item);
            }
        }

        /// <remarks>
        /// This implementation is taken from the following link:
        /// http://stackoverflow.com/a/3719617
        /// 
        /// The original code was designed around the usage scenario of wanting to be able to define what 
        /// I considered equality based on context, rather than what made two objects truely equal or identical.
        /// Ignorant to others implementing this same idea (perhaps for other motivations), the the code seemed to work.
        /// 
        /// However, I later found that improper implementation of the GetHashCode method could potentially cause problems:
        /// http://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer/3719802#3719802
        /// 
        /// Hence the new implementation.
        /// </remarks>
        private class LambdaEqualityComparer<T> : IEqualityComparer<T>
        {
            readonly Func<T, T, bool> _comparer;
            readonly Func<T, int> _hash;

            public LambdaEqualityComparer(Func<T, T, bool> comparer)
                : this( comparer, t => 0 ) // NB Cannot assume anything about how e.g., t.GetHashCode() interacts with the comparer's behavior
            {
            }

            public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash)
            {
                _comparer = comparer;
                _hash = hash;
            }

            public bool Equals(T x, T y)
            {
                return _comparer( x, y );
            }

            public int GetHashCode(T obj)
            {
                return _hash( obj );
            }
        }

        public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value, Func<TSource, TSource, bool> comparer)
        {
            return source.Contains(value, new LambdaEqualityComparer<TSource>(comparer));
        }

        public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> comparer)
        {
            return source.Distinct(new LambdaEqualityComparer<TSource>(comparer));
        }

        public static bool IsReferenceNull<TSource>(this TSource source) where TSource: class
        {
            return ReferenceEquals(source, null);
        }

        public static bool ReferenceEquals<TSource>(this TSource source, TSource item)
        {
            return object.ReferenceEquals(source, item);
        }

        public static bool ContainsReference<TSource>(this IEnumerable<TSource> source, TSource item)
        {
            foreach (var sourceItem in source)
                if(sourceItem.ReferenceEquals(item))
                    return true;
            return false;
        }

        public static void IsTrue(this bool condition, System.Action action)
        {
            if (condition) action.Invoke();
        }

        public static void IsFalse(this bool condition, System.Action action)
        {
            if (!condition) action.Invoke();
        }
    }
}